package controlP5;

import java.lang.reflect.Constructor;
import java.util.Arrays;

import processing.core.PApplet;
import processing.core.PFont;
import processing.core.PImage;

public class BitFont extends PFont {

	static public final String standard58base64 = "AakACQBgACAEAgQGBggGAgMDBAYDBAIGBQMFBQUFBQUFBQICBAUEBQgFBQUFBQUFBQIFBQQGBQUFBQUFBAUGCAUGBQMFAwYGAwQEBAQEBAQEAgQEAgYEBAQEAwQEBAQGBAQEBAIEBQKgUgghIaUAAIiRMeiZZwwAAANgjjnvmRRKESVzzDGXoqQUvYURQCCAQCCSCAAAAAgAAABEqECleCVFkRAAiLSUWEgoJQAAiSOllEJIKVRiSymllCRFSSlCEVIAQQBBQAARAAAAEAAAACQpgeALJASiIwAQSQipE1BKRS+QSEohhRBSqES1UkopSIqSkkIiFAGwEZOwSaplZGx2VVXVSQIAgeIgSETy4RCSCEnoEONAgJCkd0I6p73QiKilk46RpCQZQoQIAFBVVVOVVFVVVUKqqiqKCACCDyKpiIoAICQJ9FAiCUE8ElUphRRCSqESUUohJSRJSUpECBEAoCrqoiqZqqqqiFRVUiIJAADKI5UQASEgSAoJpSRSCgECUlJKKYSUSiWilEJKSRKRlIgQJABAVVVEVVJVVVUhqaqqQhIACBQixEIBQFBg9AwyRhhDBEIIpGPOCyZl0kXJBJOMGMImEW9owAcbMQmrpKpKxjJiopQdFQAAAAAAAABAAAAAAAAAAIAAAOAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAQIAAAEAQAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgAAAgCAAAAAgAA";

	static public final String standard56base64 = "AeYACQBgACAEAgQGBggHAgMDBgYDBQIFBgMGBgYGBgYGBgIDBAYEBggGBgYGBgYGBgIGBgUIBgYGBgYGBgYGCAYGBgMFAwYHAwUFBQUFAwUFAgMFAggFBQUFBAQEBQUIBQUFBAMEBQKgUgghRwoBAIAcOQ7yOZ/jAADAAXAe5/k+JwqKQlDkPM7jfFGUFEXfwghAQAAICIQUAgAAAAABAAAAQAkVqBSvJFJUEQCQaFHEBBEURQAAiDiiKIqCIIqCkjAWRVEURUQUJUURFCEFIBAAAgEBhAAAAABAAAAAAEikBIIvkFAQOQQAJBIEKU8ARVGiLyCRKAqiIAiioCJUTVEURQERRUmKgkQoAsAd40zcSambY447u5SSUnoSAYBAcRBMRNWHh4iEMAn0II4HBBAk6XuC6HmyL2gISVX0RI9DREoSQRAhAgBIKaW0lFIpKaWUIiSlpJRQhAAg+CCSFBFBACAiEdAHRUgEgfiIqIqiIAqCKAoqQlAWBVEBEZGSpBBCiAAAUgrpJaU0SkoppRBJKckkIxEAAJRHKkIEEACESEKERBERRUEAAVKiKIqCIIqKkhAURUGUREREJEVEECQAgJRSCkkplZJSSilIUkpKKUgEAAKFCHGhAIBAwdHnII5DOA4iIAiB6HGeL3CinOgFRU7gRA7hEDYR8QUJ+MEd40xcSqmkZI6LEWdsknsSAQAAAAAAAAAgAAAAAAAAAACAAACAAwAAAAAAAAAAAAAAQAAAAAAAAAADAwAAAAAABBAAAICAAAAAAIAAJQAAAAAAAAAABAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAACAAAgIAAAAAAYAAA=";

	static public final String grixelbase64 = "AnoADABgACAFAgQICAoIAgQEBgYDBQIKCQMICAgICAcICAIDBQYFBwkICAgIBwcICAYHCAcJCAgICAgICAgICggICAQKBAQHBAcHBwcHBQcHAgUHBAoHBwcHBgcGBwcKBwcHBQIFCAJAJeIjkENBAAAAQHzk4wPz5/Pz8QEAAB4ePj8+Pz6fX9AHCgoECvL58fnx+QsKiigo6C8CIAEIIAAAARwgEAoEAAAAAAAABAAAAAAAICIAAZVIUiERBQEAAIAIWlAQSkAQKCgIICCEhAQFBQUFAgFBBCgoMGwoKCgoKAghKCiioCCgEIAKQIAAAAQIgAAgEAAAAAAAABAAAAAAAICIsAUEfwlCRBCkEAAAIUhAQCQBAaCgIEAAAcoUFBQQFAgEBBGgoECpoqCgoKAAhKCgiEREQIIAAgAAAgAQIAACgEAAAAAAAABAAAAAAAAAIrIBEIgkgBBBEEEAAIIgAQGJ/ARAgoKS+AioVFBQQFAgEBBEgEICmZKCgoKCAhCCgiKioIAIBAgA4Pl4fJ7n+YRC8c7H8/F5ni8UiigU+okIAEAg4gOBA0HfhwcEguTDEwL0g/DxAwFAoFJ/PwFBv1/eHwH6CASKCgoKCvJBCAqKCAEBISAgAAAoFAqFQigUikREoVAoFISEUCgiSQgSQgAAgQgSAlEEEQQACAhSANAfUBAhCAiIj2BKBQUFBAUCQUEEKCQQKCzoJ+gHCCEoKCIKBIIAgQAAvlAg9AuhUOgREYVCoVBgEEKhiBghhIgAAAB/SITEEKQQABAgSAFAIEBBhCAgQABByBMUFBAUCAQFEaGgQKCgoICgECCEIJGIRBAEAggCAIRCgVAghEKhSEQUCoVCAUYIhSJihAgiAgAAiCQJFUMQAAgggCAFBIEEBRGCghACAkBAUFBQUCAQFESEggKBgoICkoKCEIIoIgpCCAhACAAQCoVCoRAKhUIRUSgUCgUhISSJSBISiAgAQCDiE4gTQQAgUAB89OcD4uND8PFJAAAEfkE/Pj++gF/Q5wn6BQryCfAJ8kHwQXAnCOEvACIAgM/j8XiCLxQKWUQhz8cXeDgPw52Q7yciAAAAAAIAANgAQAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAgAPg4AcAAAAAACAACAAAAAABEAAAAAAAACAAawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4ABgAAAAABEAAAAAAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";

	protected int characters;

	protected int[] charWidth = new int[255];

	protected int charHeight;

	protected int[][] chars;

	protected int lineHeight = 10;

	protected int kerning = 0;

	protected int wh;

	protected PImage texture;

	public BitFont(byte[] theBytes) {

		super();

		texture = decodeBitFont(theBytes);

		make();

		size = lineHeight;
		glyphs = new Glyph[256];
		ascii = new int[128];
		Arrays.fill(ascii, -1);
		lazy = false;
		ascent = 4;
		descent = 4;
		glyphCount = 128;
		for (int i = 0; i < 128; i++) {

			// unfortunately the PFont.Glyph constructor in the android source code is for whatever
			// reason protected and not public like in the java application source, therefore the
			// bitfont will only be used in the java application mode until changes to the
			// processing core code have been made. see issue
			// http://code.google.com/p/processing/issues/detail?id=1293

			try {
				Constructor<PFont.Glyph>[] constructors = (Constructor<PFont.Glyph>[]) PFont.Glyph.class.getDeclaredConstructors();
				Constructor<PFont.Glyph> constructor = (Constructor<PFont.Glyph>) PFont.Glyph.class.getDeclaredConstructors()[0];
				constructor.setAccessible(true);
				for (Constructor<PFont.Glyph> c : constructors) {
					c.setAccessible(true);
					if (c.getParameterTypes().length == 1) {
						glyphs[i] = c.newInstance(this);
						break;
					}
				}
			} catch (Exception e) {
				System.out.println(e);
			}

			// as soon as the constructor is public, the line below will replace the hack above
			// glyphs[i] = new Glyph();

			glyphs[i].value = i;

			if (glyphs[i].value < 128) {
				ascii[glyphs[i].value] = i;
			}

			glyphs[i].index = i;
			int id = i - 32;
			if (id >= 0) {
				glyphs[i].image = new PImage(charWidth[id], 9, ALPHA);
				for (int n = 0; n < chars[id].length; n++) {
					glyphs[i].image.pixels[n] = (chars[id][n] == 1) ? 0xffffffff : 0x00000000;
				}
				glyphs[i].height = 9;
				glyphs[i].width = charWidth[id];
				glyphs[i].index = i;
				glyphs[i].value = i;
				glyphs[i].setWidth = charWidth[id];
				glyphs[i].topExtent = 4;
				glyphs[i].leftExtent = 0;
			} else {
				glyphs[i].image = new PImage(1, 1);
			}
		}
	}

	public Glyph getGlyph(char c) {
		return glyphs[(int) (c)];
	}

	PImage decodeBitFont(byte[] bytes) {

		PImage tex;

		// read width
		int w = CP.byteArrayToInt(new byte[] { bytes[0], bytes[1] });

		// read height
		int h = CP.byteArrayToInt(new byte[] { bytes[2], bytes[3] });

		// read size of chars
		int s = CP.byteArrayToInt(new byte[] { bytes[4], bytes[5] });

		// read first ascii char
		int c = CP.byteArrayToInt(new byte[] { bytes[6], bytes[7] });

		tex = new PImage(w, h, PApplet.ALPHA);

		// read bytes and write pixels into image
		int off = 8 + s;
		for (int i = off; i < bytes.length; i++) {
			for (int j = 0; j < 8; j++) {
				tex.pixels[(i - off) * 8 + j] = CP.getBit(bytes[i], j) == 1 ? 0xff000000 : 0xffffffff;
			}
		}

		int cnt = 0, n = 0, i = 0;

		// add character seperators on top of the texture
		for (i = 0; i < s; i++) {
			while (++cnt != bytes[i + 8]) {
			}
			n += cnt;
			tex.pixels[n] = 0xffff0000;
			cnt = 0;
		}

		return tex;
	}

	int getHeight() {
		return texture.height;
	}

	BitFont make() {

		charHeight = texture.height;

		lineHeight = charHeight;

		int currWidth = 0;

		for (int i = 0; i < texture.width; i++) {
			currWidth++;
			if (texture.pixels[i] == 0xffff0000) {
				charWidth[characters++] = currWidth;
				currWidth = 0;
			}
		}

		chars = new int[characters][];

		int indent = 0;

		for (int i = 0; i < characters; i++) {
			chars[i] = new int[charWidth[i] * charHeight];
			for (int u = 0; u < charWidth[i] * charHeight; u++) {
				chars[i][u] = texture.pixels[indent + (u / charWidth[i]) * texture.width + (u % charWidth[i])] == 0xff000000 ? 1 : 0;
			}
			indent += charWidth[i];
		}
		return this;
	}
}
